﻿using UnityEngine;
using UnityEditor;
using System.IO;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

public class CreateScriptWindow : EditorWindow
{
	private enum ScriptType
	{
		MonoBehaviour,
		EmptyClass,
		StaticClass,
		CustomEditor,
		EditorWindow,
		ScriptableObject,
		MenuItemMethod,
		AssetPreprocessor,
		StateMachineBehaviour,
	}

	private string scriptName = "NewScript";
	private string originalTemplate;
	private string currentScript = "";
	private string unityPath = "Assets";
	private ScriptType scriptType = ScriptType.MonoBehaviour;
	private Vector2 scroll = new Vector2();
	private Dictionary<string, bool> methods = new Dictionary<string, bool>();

	private void OnFocus()
	{
		LoadTemplate(scriptType);
	}
	
	private void OnGUI()
	{
		EditorGUILayout.BeginHorizontal();
		{
			scroll = EditorGUILayout.BeginScrollView(scroll, "box", GUILayout.Width(Screen.width / 2f - 5f), GUILayout.Height(Screen.height - 30f));
			{
				EditorGUILayout.BeginHorizontal("box");
				{
					GUILayout.Label(currentScript);
				}
				EditorGUILayout.EndHorizontal();
			}
			EditorGUILayout.EndScrollView();
			EditorGUILayout.BeginVertical("box", GUILayout.Width(Screen.width / 2f - 5f), GUILayout.Height(Screen.height - 30f));
			{
				EditorGUILayout.BeginHorizontal();
				{
					GUILayout.Label("Script Name");
					scriptName = EditorGUILayout.TextField(scriptName);
					GUILayout.Label("Name ex: " + FindNameExample(originalTemplate), EditorStyles.miniBoldLabel);
				}
				EditorGUILayout.EndHorizontal();
				
				ScriptType type = (ScriptType)EditorGUILayout.EnumPopup("Script Type", scriptType);
				if (type != scriptType)
				{
					scriptType = type;
					LoadTemplate(type);
				}
				
				EditorGUILayout.BeginHorizontal();
				{
					GUILayout.Label("Choose Methods");
					EditorGUILayout.BeginVertical("box");
					{
						List<string> modified = new List<string>();
						foreach (KeyValuePair<string, bool> kvp in methods)
						{
							bool b = EditorGUILayout.ToggleLeft(" " + kvp.Key, kvp.Value);
							if (b != kvp.Value)
							{
								modified.Add(kvp.Key);
							}
						}
						
						modified.ForEach(x => { methods[x] = !methods[x]; });
					}
					EditorGUILayout.EndVertical();
				}
				EditorGUILayout.EndHorizontal();

				unityPath = "Assets";
				Object[] selection = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets);
				if (selection.Length > 0)
				{
					unityPath = AssetDatabase.GetAssetPath(selection[0]);
					if (Path.GetExtension(unityPath) != "")
					{
						unityPath = Path.GetDirectoryName(unityPath);
					}
				}

				GUILayout.Label("Will create script here : " + unityPath);
				GUILayout.FlexibleSpace();
				EditorGUILayout.BeginHorizontal();
				{
					GUILayout.FlexibleSpace();
					if (GUILayout.Button("CREATE SCRIPT", GUILayout.Width(150f), GUILayout.Height(80f)))
					{
						string path = unityPath + "/" + scriptName + ".cs";
						if (!File.Exists(path))
						{
							StreamWriter writer = File.CreateText(path);
							writer.WriteLine(currentScript);
							writer.Close();

							AssetDatabase.Refresh();
						}
					}
				}
				EditorGUILayout.EndHorizontal();
			}
			EditorGUILayout.EndVertical();
		}
		EditorGUILayout.EndHorizontal();
		
		if (GUI.changed)
		{
			SaveMethods(scriptType.ToString(), methods);
			currentScript = CreateScript(originalTemplate, scriptName, methods);
		}
	}

	private void LoadTemplate(ScriptType _type)
	{
		originalTemplate = LoadScriptTemplate(_type);
		methods = FindAllMethods(originalTemplate);
		methods = LoadFromSave(_type.ToString(), methods);

		currentScript = CreateScript(originalTemplate, scriptName, methods);
	}

	private string CreateScript(string _template, string _name, Dictionary<string, bool> _methods)
	{
		string script = _template.Replace("$SCRIPTNAME$", _name);

		foreach (KeyValuePair<string, bool> kvp in _methods)
		{
			if (!kvp.Value)
			{
				script = RemoveMethod(script, kvp.Key);
			}
		}

		script = RemoveTags(script);

		return script;
	}

	private string RemoveMethod(string _script, string _method)
	{
		int n = 0;
		while (n >= 0)
		{
			n = _script.IndexOf("<method", n);
			if (n >= 0)
			{
				int start = _script.IndexOf(":", n);
				int seperation = _script.IndexOf(";", n);
				int end = Mathf.Min(seperation < 0 ? int.MaxValue : seperation, _script.IndexOf(">", n));
				
				string name = _script.Substring(start + 1, end - start - 1);
				name = name.Replace(" ", "");

				if (name == _method)
				{
					int nextMethod = _script.IndexOf("<method", n + 1);
					int closingTag = _script.IndexOf("</method>", n) + 11;
					string newScript = _script.Remove(n, Mathf.Max(nextMethod, closingTag) - n);
					return newScript;
				}
				
				n = end;
			}
		}

		return _script;
	}

	private string RemoveTags(string _script)
	{
		string newScript = _script;
		int n = 0;
		while (n >= 0)
		{
			n = newScript.IndexOf("<", n);
			if (n >= 0)
			{
				int end = newScript.IndexOf(">", n) + 3;
				newScript = newScript.Remove(n, end - n);
			}
		}
		
		return newScript;
	}

	private string LoadScriptTemplate(ScriptType _type)
	{
		string path = "Assets/Editor/CreateScripts/template/" + _type.ToString() + "-template.txt";
		TextAsset asset = (TextAsset)AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset));
		if (asset != null)
		{
			return asset.text;
		}
		else
		{
			return "";
		}
	}

	private Dictionary<string, bool> FindAllMethods(string _template)
	{
		Dictionary<string, bool> dictio = new Dictionary<string, bool>();
		int n = 0;
		while (n >= 0)
		{
			n = _template.IndexOf("<method", n);
			if (n >= 0)
			{
				int start = _template.IndexOf(":", n);
				int seperation = _template.IndexOf(";", n);
				int end = Mathf.Min(seperation < 0 ? int.MaxValue : seperation, _template.IndexOf(">", n));

				string name = _template.Substring(start + 1, end - start - 1);
				name = name.Replace(" ", "");
				dictio.Add(name, seperation >= 0 && seperation <= end);

				n = end;
			}
		}

		return dictio;
	}
	
	private Dictionary<string, bool> LoadFromSave(string _scriptName, Dictionary<string, bool> _dictio)
	{
		string save = "CreateScript." + _scriptName;
		if (EditorPrefs.GetBool(save, false))
		{
			Dictionary<string, bool> newDictio = new Dictionary<string, bool>();
			foreach (KeyValuePair<string, bool> kvp in _dictio)
			{
				newDictio.Add(kvp.Key, EditorPrefs.GetBool(save + "." + kvp.Key, false));
			}

			return newDictio;
		}
		else
		{
			EditorPrefs.SetBool(save, true);
			foreach (KeyValuePair<string, bool> kvp in _dictio)
			{
				EditorPrefs.SetBool(save + "." + kvp.Key, kvp.Value);
			}
		}

		return _dictio;
	}

	private void SaveMethods(string _scriptName, Dictionary<string, bool> _dictio)
	{
		string save = "CreateScript." + _scriptName;
		foreach (KeyValuePair<string, bool> kvp in _dictio)
		{
			EditorPrefs.SetBool(save + "." + kvp.Key, kvp.Value);
		}
	}

	private string FindNameExample(string _template)
	{
		int n = _template.IndexOf("<example_name");
		int start = _template.IndexOf(":", n);
		int end = _template.IndexOf(">", n);
		return _template.Substring(start, end - start);
	}
}
